package cn.demoncat.util.word;

import cn.demoncat.util.exception.IoRuntimeException;
import cn.demoncat.util.exception.ParamRuntimeException;
import cn.demoncat.util.io.CloseUtil;
import cn.demoncat.util.lang.IntegerUtil;
import cn.demoncat.util.lang.ObjectUtil;
import cn.demoncat.util.lang.StringUtil;
import cn.demoncat.util.lang.constant.StatusConstant;
import cn.demoncat.util.lang.entity.data.IntegerPoint;
import cn.demoncat.util.word.constant.WordGenConstant;
import cn.demoncat.util.word.entity.*;
import ognl.Ognl;
import ognl.OgnlException;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.impl.xb.xmlschema.SpaceAttribute;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.List;

/**
 * Word生成工具
 *
 * 文档结构：文档[内容(段落[数据] | 表格[表头,数据])
 *
 * @author 延晓磊
 * @since 2021年04月09日
 */
public class WordGenUtil {



    /**
     * 测试模板文档的样式，以便查找样式ID来设置段落
     *
     * @author 延晓磊
     *
     * @since 2017年8月12日
     */
    public static void checkStyles() {
        //获取模板文档的所有样式
        for (CTStyle e: WordGenConstant.Style.TEMPLATE_STYLE.getStyleList()) {
            //查看样式的XML详情，w:styleId 即样式ID，可以设置给段落 XWPFParagraph
            System.out.println(e);
        }
    }

    /**
     * 生成文档
     *
     * @param doc
     * @param out
     *
     * @author 延晓磊
     * @since 2021年04月10日
     */
    public static void out(WordDocument doc, OutputStream out){
        // 创建文档
        XWPFDocument document = createDocument();
        // 设置纸张
        if (StringUtil.isBlank(doc.getPaper())) {
            doc.setPaper(WordGenConstant.Paper.A4);
        }else{
            setPaper(document, doc.getPaper());
        }
        // 设置页眉
        if (StringUtil.isNotBlank(doc.getHeader())) {
            setHeader(document,doc);
        }
        // 设置页脚（页码）
        if (StatusConstant.Common.isYes(doc.getFooter())) {
            setFooter(document);
        }
        // 添加内容
        if (CollectionUtils.isNotEmpty(doc.getContents())) {
            for (WordContent content : doc.getContents()) {
                addContent(document, content);
            }
        }
        // 输出文档
        outDocument(document, out);
    }


    /**
     * 创建文档
     *
     * @return
     *
     * @author 延晓磊
     * @since 2021年04月09日
     */
    private static XWPFDocument createDocument(){
        // 创建文档
        XWPFDocument document = new XWPFDocument();
        // 设置文档的样式
        document.createStyles().setStyles(WordGenConstant.Style.TEMPLATE_STYLE);
        return document;
    }

    /**
     * 输出文档
     *
     * @param document
     * @param out
     *
     * @author 延晓磊
     * @since 2021年04月09日
     */
    private static void outDocument(XWPFDocument document, OutputStream out){
        try {
            document.write(out);
        } catch (IOException e) {
            throw new IoRuntimeException("导出Word文档失败", e);
        }finally {
            // 关闭流
            CloseUtil.flush(out);
            CloseUtil.close(document);
            CloseUtil.close(out);
        }
    }

    /**
     * 创建纸张
     *
     * @param document
     *
     * @author 延晓磊
     * @since 2021年04月10日
     */
    private static CTSectPr createPaper(XWPFDocument document){
        CTBody ctBody = document.getDocument().getBody();
        return ctBody.isSetSectPr() ? ctBody.getSectPr() : ctBody.addNewSectPr();
    }

    /**
     * 设置纸张
     *
     * @param document
     * @param width     宽 pt
     * @param height    高 pt
     */
    private static void setPaper(XWPFDocument document, int width, int height){
        // 创建纸张
        CTSectPr sectPr = createPaper(document);
        // 设置尺寸
        CTPageSz pgSz = sectPr.addNewPgSz();
        pgSz.setW(BigInteger.valueOf(width));
        pgSz.setH(BigInteger.valueOf(height));
    }

    /**
     * 设置纸张
     *
     * @param document
     * @param paper     纸张类型：WordGenConstant.Paper
     */
    private static void setPaper(XWPFDocument document, String paper){
        // 映射纸张
        IntegerPoint point = WordGenConstant.Paper.Map.SIZE.get(paper);
        // 设置尺寸
        setPaper(document, point.getX(), point.getY());
    }

    /**
     * 创建页眉
     *
     * @param document
     * @param doc
     *
     * @author 延晓磊
     * @since 2021年04月10日
     */
    private static void setHeader(XWPFDocument document, WordDocument doc){
        // 创建代理
        XWPFHeaderFooterPolicy headerFooterPolicy = new XWPFHeaderFooterPolicy(document, createPaper(document));
        // 创建页眉
        XWPFHeader header = headerFooterPolicy.createHeader(XWPFHeaderFooterPolicy.DEFAULT);
        // 创建段落
        XWPFParagraph paragraph = header.createParagraph();
        paragraph.setAlignment(ParagraphAlignment.CENTER);
        if (StatusConstant.Common.isYes(doc.getHeaderBorder())) {
            // 边框下划线
            paragraph.setBorderBottom(Borders.SINGLE);
        }
        // 创建标题
        XWPFRun run = paragraph.createRun();
        run.setText(doc.getHeader());
    }

    /**
     * 设置页脚（页码）
     *
     * @param document
     *
     * @author 延晓磊
     * @since 2021年04月10日
     */
    private static void setFooter(XWPFDocument document){
        // 创建代理
        XWPFHeaderFooterPolicy headerFooterPolicy = new XWPFHeaderFooterPolicy(document, createPaper(document));
        // 创建页脚
        XWPFFooter footer = headerFooterPolicy.createFooter(XWPFHeaderFooterPolicy.DEFAULT);
        // 创建段落
        XWPFParagraph paragraph = footer.createParagraph();
        paragraph.setAlignment(ParagraphAlignment.CENTER);
        // 创建页码
        XWPFRun run = paragraph.createRun();
        run.setText("第 ");
        // - 当前页
        run.getCTR().addNewPgNum();
        run.setText(" 页 共 ");
        // - 总页数
        createPageSize(run);
        run.setText(" 页");
    }

    /**
     * 创建页数
     *
     * @param run
     */
    private static void createPageSize(XWPFRun run){
        // 脚本开始
        CTFldChar fldChar = run.getCTR().addNewFldChar();
        fldChar.setFldCharType(STFldCharType.BEGIN);
        // 脚本
        CTText ctText = run.getCTR().addNewInstrText();
        /*
         * 页号："PAGE"
         * 页数："NUMPAGES"
         * 格式："\\* MERGEFORMAT"
         */
        ctText.setStringValue("NUMPAGES \\* MERGEFORMAT");
        ctText.setSpace(SpaceAttribute.Space.PRESERVE);
        // 脚本结束
        fldChar = run.getCTR().addNewFldChar();
        fldChar.setFldCharType(STFldCharType.END);
    }

    /**
     * 创建段落
     *
     * @param document
     * @param style     段落样式：WordGenConstant.Style
     * @return
     *
     * @author 延晓磊
     * @since 2021年04月09日
     */
    private static XWPFParagraph createParagraph(XWPFDocument document, String style){
        // 创建段落
        XWPFParagraph paragraph = document.createParagraph();
        // 设置样式
        paragraph.setStyle(WordGenConstant.Style.STYLE_ID_MAP.get(StringUtil.toDefault(style, WordGenConstant.Style.NORMAL)));
        return paragraph;
    }

    /**
     * 创建表格
     *
     * @param document
     * @param table
     * @p
     * @return
     *
     * @author 延晓磊
     * @since 2021年04月09日
     */
    private static XWPFTable createTable(XWPFDocument document, WordTable table){
        // 创建表格
        XWPFTable xTable = document.createTable(table.rowSize(), table.colSize());
        xTable.setTableAlignment(TableRowAlign.CENTER);
        xTable.setWidthType(TableWidthType.PCT);
        xTable.setWidth("100%");
        // 设置单元格边距
        Integer[] margins = ObjectUtil.toDefault(table.getCellMargins(), WordGenConstant.ContentType.Table.CELL_MARGIN);
        xTable.setCellMargins(margins[0],margins[1],margins[2],margins[3]);
        return xTable;
    }

    /**
     * 添加内容
     *
     * @param document
     * @param content
     *
     * @author 延晓磊
     * @since 2021年04月12日
     */
    private static void addContent(XWPFDocument document, WordContent content){
        // 内容类型
        if (StringUtil.isBlank(content.getType())) {
            content.setType(WordGenConstant.ContentType.PARAGRAPH);
        }
        switch (content.getType()) {
            case WordGenConstant.ContentType.PARAGRAPH:
                // 段落非空
                if (content.getParagraph() != null) {
                    // 创建段落
                    XWPFParagraph xParagraph = createParagraph(document, content.getParagraph().getStyle());
                    // 对齐方式
                    if (StringUtil.isNotBlank(content.getParagraph().getAlign())) {
                        xParagraph.setAlignment(WordGenConstant.Align.VALUE_MAP.get(content.getParagraph().getAlign()));
                    }
                    // 断页
                    if (StatusConstant.Common.isYes(content.getParagraph().getPageBreak())) {
                        xParagraph.setPageBreak(true);
                    }
                    // 添加数据
                    if (CollectionUtils.isNotEmpty(content.getParagraph().getRuns())) {
                        for (WordRun run : content.getParagraph().getRuns()) {
                            addRun(xParagraph, run);
                        }
                    }
                }
                break;
            case WordGenConstant.ContentType.TABLE:
                // 表格非空，表头非空，行非空（展示表头 || 数据为空）
                if (content.getTable() != null && CollectionUtils.isNotEmpty(content.getTable().getHeaders()) && content.getTable().rowSize() > 0) {
                    // 创建表格
                    XWPFTable table = createTable(document, content.getTable());
                    // 添加内容
                    if (StatusConstant.Common.isNo(content.getTable().getHeaderStatus())) {
                        // 添加表体（不展示表头 && 数据非空）
                        try {
                            addTableBody(table.getRows(), content.getTable());
                        } catch (OgnlException e) {
                            throw new ParamRuntimeException("导出Word文档失败：表格数据错误", e);
                        }
                    }else{
                        // 添加表头
                        addTableHeader(table.getRow(0), content.getTable());
                        // 添加表体
                        if (content.getTable().dataSize() > 0) {
                            try {
                                addTableBody(table.getRows().subList(1, table.getRows().size()), content.getTable());
                            } catch (OgnlException e) {
                                throw new ParamRuntimeException("导出Word文档失败：表格数据错误", e);
                            }
                        }
                    }
                }
                break;
            default:
                break;
        }
    }


    /**
     * 创建数据
     *
     * @param paragraph
     * @param url       超链地址
     * @return
     *
     * @author 延晓磊
     * @since 2021年04月09日
     */
    private static XWPFRun createRun(XWPFParagraph paragraph, String url){
        if (StringUtil.isBlank(url)) {
            // 普通数据
            return paragraph.createRun();
        }else{
            // 链接数据
            return paragraph.createHyperlinkRun(url);
        }
    }

    /**
     * 添加数据
     *
     * @param paragraph 段落
     * @param run   段落的主数据
     *
     * @author 延晓磊
     * @since 2021年04月10日
     */
    private static void addRun(XWPFParagraph paragraph, WordRun run){
        // 创建数据
        XWPFRun xRun = createRun(paragraph, run.getUrl());
        // 设置样式
        if (run.getStyle() != null) {
            // 颜色
            if (StringUtil.isNotBlank(run.getStyle().getColor())) {
                xRun.setColor(run.getStyle().getColor());
            }
            // 比例
            if (!IntegerUtil.isEmpty(run.getStyle().getScale())) {
                xRun.setTextScale(run.getStyle().getScale());
            }
            // 字号
            if (StringUtil.isNotBlank(run.getStyle().getSize())) {
                xRun.setFontSize(WordGenConstant.FontSize.PT_MAP.get(run.getStyle().getSize()));
            }
            // 垂直对齐
            if (StringUtil.isNotBlank(run.getStyle().getAlign())) {
                xRun.setVerticalAlignment(WordGenConstant.VerticalAlign.VALUE_MAP.get(run.getStyle().getAlign()));
            }
            // 加粗
            if (StatusConstant.Common.isYes(run.getStyle().getBold())) {
                xRun.setBold(true);
            }else if (StatusConstant.Common.isNo(run.getStyle().getBold())) {
                xRun.setBold(false);
            }
            // 斜体
            if (StatusConstant.Common.isYes(run.getStyle().getItalic())) {
                xRun.setItalic(true);
            }else if (StatusConstant.Common.isNo(run.getStyle().getItalic())) {
                xRun.setItalic(false);
            }
            // 删除线
            if (StatusConstant.Common.isYes(run.getStyle().getStrike())) {
                xRun.setStrikeThrough(true);
            }else if (StatusConstant.Common.isNo(run.getStyle().getStrike())) {
                xRun.setStrikeThrough(false);
            }
            // 下划线
            if (StatusConstant.Common.isYes(run.getStyle().getUnderline())) {
                // 字符下方，单实线
                xRun.setUnderline(UnderlinePatterns.SINGLE);
            }else if (StatusConstant.Common.isNo(run.getStyle().getUnderline())) {
                xRun.setUnderline(UnderlinePatterns.NONE);
            }
        }
        // 数据类型
        if (CollectionUtils.isNotEmpty(run.getDatas())) {
            // 多条数据
            for (WordRun data : run.getDatas()) {
                addRun(xRun, data);
            }
        }else{
            // 单条数据
            addRun(xRun, run);
        }
    }

    /**
     * 添加数据 - 断点
     *
     * @param run
     * @param type  类型：WordGenConstant.RunBreak
     *
     * @author 延晓磊
     * @since 2021年04月10日
     */
    private static void addRunBreak(XWPFRun run, String type){
        if (StringUtil.isBlank(type)) {
            run.addBreak();
        }else{
            switch (type) {
                case WordGenConstant.RunBreak.TAB:
                    // 制表符
                    run.addTab();
                    break;
                case WordGenConstant.RunBreak.PAGE:
                    // 换页
                    run.addBreak(BreakType.PAGE);
                    break;
                default:
                    // 换行
                    run.addBreak();
                    break;
            }
        }
    }

    /**
     * 添加数据 - 文本
     *
     * @param run
     * @param text
     *
     * @author 延晓磊
     * @since 2021年04月10日
     */
    private static void addRunText(XWPFRun run, String text){
        // 添加文本
        run.setText(StringUtil.toEmpty(text));
    }

    /**
     * 添加数据
     *
     * @param xRun
     * @param run
     *
     * @author 延晓磊
     * @since 2021年04月10日
     */
    private static void addRun(XWPFRun xRun, WordRun run){
        // 默认文本类型
        if (StringUtil.isBlank(run.getType())) {
            run.setType(WordGenConstant.RunType.TEXT);
        }
        // 数据类型
        switch (run.getType()) {
            case  WordGenConstant.RunType.TEXT:
                // 文本
                addRunText(xRun, run.getData());
                break;
            case  WordGenConstant.RunType.BREAK:
                // 断点
                addRunBreak(xRun, run.getData());
                break;
            default:
                break;
        }
    }

    /**
     * 添加表头
     *
     * @param row
     * @param table
     *
     * @author 延晓磊
     * @since 2021年04月12日
     */
    private static void addTableHeader(XWPFTableRow row, WordTable table){
        // 遍历列
        WordTableHeader header;
        XWPFTableCell cell;
        XWPFParagraph paragraph;
        XWPFRun run;
        for (int i = 0; i < table.colSize(); i++) {
            header = table.getHeaders().get(i);
            cell = row.getCell(i);
            // 设置列宽
            if (!IntegerUtil.isEmpty(header.getWidth())) {
                cell.setWidth(header.getWidth() + "%");
            }
            // 设置段落
            paragraph = cell.getParagraphArray(0);
            paragraph.setStyle(WordGenConstant.Style.STYLE_ID_MAP.get(WordGenConstant.Style.NORMAL));
            paragraph.setAlignment(WordGenConstant.Align.VALUE_MAP.get(StringUtil.toDefault(header.getHeaderAlign(), WordGenConstant.Align.CENTER)));
            // 创建数据
            run = paragraph.createRun();
            // 数据 - 标题
            run.setText(StringUtil.toDefault(header.getTitle(), header.getField()));
            // 数据 - 颜色
            if (StringUtil.isNotBlank(header.getHeaderColor())) {
                run.setColor(header.getHeaderColor());
            }
            // 数据 - 字号
            if (StringUtil.isNotBlank(header.getHeaderSize())) {
                run.setFontSize(WordGenConstant.FontSize.PT_MAP.get(header.getHeaderSize()));
            }
            // 数据 - 加粗
            if (!StatusConstant.Common.isNo(header.getHeaderBold())) {
                run.setBold(true);
            }
        }
    }

    /**
     * 添加表体
     *
     * @param rows
     * @param table
     *
     * @author 延晓磊
     * @since 2021年04月12日
     */
    private static void addTableBody(List<XWPFTableRow> rows, WordTable table) throws OgnlException {
        // 遍历列
        WordTableHeader header;
        XWPFTableCell cell;
        XWPFParagraph paragraph;
        XWPFRun run;
        for (int i = 0; i < table.colSize(); i++) {
            header = table.getHeaders().get(i);
            // 遍历行
            for (int j = 0; j < table.dataSize(); j++) {
                cell = rows.get(j).getCell(i);
                // 设置列宽
                if (!IntegerUtil.isEmpty(header.getWidth())) {
                    cell.setWidth(header.getWidth() + "%");
                }
                // 创建段落
                paragraph = cell.getParagraphArray(0);
                paragraph.setStyle(WordGenConstant.Style.STYLE_ID_MAP.get(WordGenConstant.Style.NORMAL));
                paragraph.setAlignment(WordGenConstant.Align.VALUE_MAP.get(StringUtil.toDefault(header.getBodyAlign(), WordGenConstant.Align.CENTER)));
                // 创建数据
                run = paragraph.createRun();
                // 数据 - 内容
                run.setText(StringUtil.toEmpty(StringUtil.toEmptyString(Ognl.getValue(header.getField(), table.getDatas().get(j)))));
                // 数据 - 颜色
                if (StringUtil.isNotBlank(header.getBodyColor())) {
                    run.setColor(header.getBodyColor());
                }
                // 数据 - 字号
                if (StringUtil.isNotBlank(header.getBodySize())) {
                    run.setFontSize(WordGenConstant.FontSize.PT_MAP.get(header.getBodySize()));
                }
                // 数据 - 加粗
                if (StatusConstant.Common.isYes(header.getBodyBold())) {
                    run.setBold(true);
                }
            }
        }
    }



}
